// SKNotes - a note taking and gathering tool
// Copyright (C) 2009  Stefan Kueng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


namespace SKNotes.Configuration
{
    using System;
    using System.Windows;
    using System.Diagnostics;

	public class DependencyPropertySetting : ValueSettingBase
	{

		public static readonly DependencyProperty ApplicationSettingsProperty = 
			DependencyProperty.RegisterAttached(
				"ApplicationSettings",
				typeof( ApplicationSettings ),
				typeof( DependencyPropertySetting ) );

		public static readonly DependencyProperty PropertyProperty =
			DependencyProperty.RegisterAttached(
				"Property",
				typeof( DependencyProperty ),
				typeof( DependencyPropertySetting ) ,
				new FrameworkPropertyMetadata( new PropertyChangedCallback( OnDependencyPropertyChanged ) ) );

		public DependencyPropertySetting( DependencyObject dependencyObject, DependencyProperty dependencyProperty ) :
			this( dependencyObject, dependencyProperty, null )
		{
		}

        
        public DependencyPropertySetting( DependencyObject dependencyObject, DependencyProperty dependencyProperty, object defaultValue ) :
			this( dependencyProperty.Name, dependencyObject, dependencyProperty, defaultValue )
		{
		}

        public DependencyPropertySetting( string name, DependencyObject dependencyObject, DependencyProperty dependencyProperty ) :
			this( name, dependencyObject, dependencyProperty, null )
		{
		}

        public DependencyPropertySetting( string name, DependencyObject dependencyObject, DependencyProperty dependencyProperty, object defaultValue ) :
			base( name, defaultValue )
		{
			if ( dependencyObject == null )
			{
				throw new ArgumentNullException( "dependencyObject" );
			}
			if ( dependencyProperty == null )
			{
				throw new ArgumentNullException( "dependencyProperty" );
			}

			this.dependencyObject = dependencyObject;
			this.dependencyProperty = dependencyProperty;
		} 

		public DependencyProperty DependencyProperty
		{
			get { return this.dependencyProperty; }
		}

		public DependencyObject DependencyObject
		{
			get { return this.dependencyObject; }
		}

		public override object OriginalValue
		{
			get { return LoadValue( Name, this.dependencyProperty.PropertyType, SerializeAs, DefaultValue ); }
		}

		public override object Value
		{
			get { return this.dependencyObject.GetValue( this.dependencyProperty ); }
			set { this.dependencyObject.SetValue( this.dependencyProperty, value ); }
		}

		public static DependencyProperty GetProperty( DependencyObject obj )
		{
			return obj.GetValue( PropertyProperty ) as DependencyProperty;
		}

		public static void SetProperty( DependencyObject obj, DependencyProperty dependencyProperty )
		{
			obj.SetValue( PropertyProperty, dependencyProperty );
		}

		public static ApplicationSettings GetApplicationSettings( DependencyObject obj )
		{
			return obj.GetValue( PropertyProperty ) as ApplicationSettings;
		} 

        public static void SetApplicationSettings( DependencyObject obj, ApplicationSettings applicationSettings )
		{
			obj.SetValue( PropertyProperty, applicationSettings );
		}

		public override void Load()
		{
			try
			{
				object originalValue = OriginalValue;
				if ( originalValue == null && LoadUndefinedValue == false )
				{
					return;
				}
				Value = originalValue;
			}
			catch
			{
				if ( ThrowOnErrorLoading )
				{
					throw;
				}
			}
		}

		public override void Save()
		{
			try
			{
				object value = Value;
				if ( value == null && SaveUndefinedValue == false )
				{
					return;
				}
				SaveValue( Name, this.dependencyProperty.PropertyType, SerializeAs, value, DefaultValue );
			}
			catch
			{
				if ( ThrowOnErrorSaving )
				{
					throw;
				}
			}
		}

		private static void OnDependencyPropertyChanged( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e )
		{
			IFrameworkInputElement frameworkInputElement = dependencyObject as IFrameworkInputElement;
			if ( frameworkInputElement == null )
			{
				Debug.WriteLine( "DependencyPropertySetting: invalid framework element" );
				return;
			}

			string elementName = frameworkInputElement.Name;
			if ( string.IsNullOrEmpty( elementName ) )
			{
				Debug.WriteLine( "DependencyPropertySetting: missing name for framework element " + frameworkInputElement );
				return; // name is required
			}

			DependencyProperty dependencyProperty = e.NewValue as DependencyProperty;
			if ( dependencyProperty == null )
			{
				Debug.WriteLine( "DependencyPropertySetting: missing dependency property" );
				return;
			}

			// search on the parent-tree for application settings
			ApplicationSettings applicationSettings = FindParentSettings( dependencyObject );
			if ( applicationSettings == null )
			{
				Debug.WriteLine( "DependencyPropertySetting: missing application settings in parent hierarchy" );
				return;
			}

			string settingName = string.Concat( elementName, ".", dependencyProperty.Name );
			applicationSettings.Settings.Add(
				new DependencyPropertySetting( settingName, dependencyObject, dependencyProperty ) );
		} 

		private static ApplicationSettings FindParentSettings( DependencyObject element )
		{
			while ( element != null )
			{
				ApplicationSettings applicationSettings = element.ReadLocalValue( ApplicationSettingsProperty ) as ApplicationSettings;
				if ( applicationSettings != null )
				{
					return applicationSettings;
				}
				element = LogicalTreeHelper.GetParent( element );
			}
			return null;
		}

		private readonly DependencyObject dependencyObject;
		private readonly DependencyProperty dependencyProperty;

	}
} 
